package com.hd.shiro2204.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hd.shiro2204.entity.Perms;
import com.hd.shiro2204.entity.Role;
import com.hd.shiro2204.entity.User;
import com.hd.shiro2204.service.UserService;
import com.hd.shiro2204.utils.ApplicationContextUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/*
* 使用shiro时需要提供一个自定义Realm对象，用于向shiro提供 身份信息和权限信息
* */
@Slf4j
public class CustomRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

    /**
     * 该方法用于获取当前用户的角色和权限信息
     * @param principalCollection
     * @return
     */
    @Override

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //1．获取当前用户的身份信息
        //注意;这返回的对象取决于 doGetAuthenticationInfo方法返回值的第一个参数

        final User principal = (User) principalCollection.getPrimaryPrincipal();
        //2．根姆user对象小的信息，去数姻库小查询该用户的角色和权限信息
        List<String> roles  = getRolesFromDB(principal);
        List<String> permissions = getPermissionsFromDB(principal);
        //3．把查询到的角色和权限信总返回给shiro，由shiro去做后续的判断处理
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRoles(roles);//设置当前用户角色信息
        simpleAuthorizationInfo.addStringPermissions(permissions);//设置当前用户权限信息

        return simpleAuthorizationInfo;

    }

    /**
     * 该方法提供用户的身份认证信息
     * 包含用户的密码
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @SneakyThrows
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //获取当前请求中页面传递过来的用户名
        String userName = usernamePasswordToken.getUsername();
        //根据 token对象中的用户名，查询该用户对应的信息（一定要包含该用户名的真正的密码)
        User user=getUserFromDB (userName);
        //判断user是否为null
        if (user==null){
            //用户名不存在
            throw new Exception("用户名不存在");
        }
        //注意:校验密码的过程，不需要我们自己写代码了，只要把该用户数据库中的密码传给shiro即可，shiro后犊的流程中会校验
        //需要我们返会AuthenticationInfo对象即可
        /*
                构造函数中的参数
                参数1:代表当前用户的身份信息的对象，可以放入user对象也可以放入用户名
                参数2:数据库中的密码
                参数3;盐，注意类型需要与shiro的参数类型匹配，固定写法ByteSource.Util.bytes (user.getSalt())
                参数4:当前realm的名字，固定写法
          */
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                user,
                user.getPassword()
                ,ByteSource.Util.bytes(user.getSalt())
                ,getName());

        return simpleAuthenticationInfo;
    }

    /**
     * 从数据库中查询用户信息
     * @param username
     * @return
     */
    private User getUserFromDB(String username) {
        /* User user = new User();
       QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        Map<String, Object> map = userService.getMap(wrapper);
        System.out.println(map);
        user.setId((Integer) map.get("id"));
        user.setUsername(username);
        user.setPassword((String) map.get("password"));*/
       /* user.setEmail((String) map.get("email"));
        user.setPhone((String) map.get("phone"));*/
        UserService userService = ApplicationContextUtils.getBean(UserService.class);
        User user = userService.getOne(new QueryWrapper<User>().eq("username", username));

        return user;
    }

    private List<String> getPermissionsFromDB(User principal) {
        List<String> roles = principal.getRoles().stream().map(Role::getName).collect(Collectors.toList());
        if (roles !=null) {
            return roles;
        }

        final UserService userService = ApplicationContextUtils.getBean(UserService.class);
        List<String> list = userService.findRoles(principal);

        for (int i = 0; i < list.size(); i++) {
            roles.add(list.get(i));
        }
        return list;
    }

    private List<String> getRolesFromDB(User principal) {

        //1．检查是否已经缓存过当前登录用户的角色信忌，如果有直接使用缓存数招
        List<String>  perms =principal.getPerms().stream().map(Perms::getName).collect(Collectors.toList());
        if (perms !=null) {
            return perms;
        }
        //访问db
        final UserService userService = ApplicationContextUtils.getBean(UserService.class);
        List<String> permissions = userService.findPermissions(principal);
        //存入缓存
        for (int i = 0; i < permissions.size(); i++) {
            perms.add(permissions.get(i));
        }
        return permissions;
//        return Arrays.asList("admin","user");

    }
}
